package com.yue.modbus.client;

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.yue.modbus.domain.Device;
import com.yue.modbus.service.MsgCallBack;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.util.List;

@Slf4j
public class ModbusClient {

    private final String ip;
    private final int port;
    ModbusMaster modbusMaster;

    private Thread heartTh;// 心跳线程
    private int state = 0;
    private List<Device> modbusConfig;
    private MsgCallBack msgCallBack;

    public void setModbusConfig(List<Device> modbusConfig) {
        this.modbusConfig = modbusConfig;
    }

    public void setMsgCallBack(MsgCallBack msgCallBack) {
        this.msgCallBack = msgCallBack;
    }

    public ModbusClient(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public String getIp() {
        return ip;
    }

    public int getPort() {
        return port;
    }


    public void connect() {
        try {
            // 设置主机TCP参数
            TcpParameters tcpParameters = new TcpParameters();

            // 设置TCP的ip地址
            InetAddress adress = InetAddress.getByName(ip);

            // TCP参数设置ip地址
            // tcpParameters.setHost(InetAddress.getLocalHost());
            tcpParameters.setHost(adress);

            // TCP设置长连接
            tcpParameters.setKeepAlive(true);
            // TCP设置端口，这里设置是默认端口502
            tcpParameters.setPort(this.port == 0 ? Modbus.TCP_PORT : this.port);

            // 创建一个主机
            modbusMaster = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
            Modbus.setAutoIncrementTransactionId(true);
            modbusMaster.connect();// 开启连接

        } catch (Exception ex) {
            log.error("modbusTcp connect失败", ex);
        }
        //开启线程实现数据的首发
        heartTh = new Thread(new HeartImplements(), "modbus-heart");
        state = 1;
        heartTh.start();
    }


    public void stop() {
        if (modbusMaster.isConnected()) {
            try {
                modbusMaster.disconnect();// 断开连接
            } catch (Exception ex) {
                log.error("modbusTcp关闭失败", ex);
            }
        }
    }


    public void destroy() {
        state = 0;
        heartTh.interrupt();
        if (modbusMaster.isConnected()) {
            try {
                modbusMaster.disconnect();// 开启连接
            } catch (ModbusIOException e) {
                e.printStackTrace();
            }
        }
        modbusMaster = null;
    }

    /**
     * 断网心跳检测类
     */
    class HeartImplements implements Runnable {

        // 2):在A类中覆盖Runnable接口中的run方法.
        public void run() {
            // 3):在run方法中编写需要执行的操作
            while (state == 1) {
                try {
                    //不在连接状态，则启动连接
                    if (modbusMaster.isConnected()) {
                        // 读取对应从机的数据
                        for (int i = 0; i < modbusConfig.size(); i++) {
                            try {
                                boolean[] registerValues = modbusMaster.readDiscreteInputs(
                                        modbusConfig.get(i).getSlavId(),
                                        modbusConfig.get(i).getOffset(),
                                        1
                                );
                                modbusConfig.get(i).setPreValue(modbusConfig.get(i).getCurValue());
                                modbusConfig.get(i).setCurValue(registerValues[0]);
                            } catch (Exception e) {
                                log.error("slavId" + modbusConfig.get(i).getSlavId() + "读取数据失败", e);
                            }

                        }
                        if (msgCallBack != null)
                            msgCallBack.onSuccess(modbusConfig);
                    } else {
                        Thread.sleep(1000);
                        modbusMaster.disconnect();
                        Thread.sleep(1000);
                        try {
                            modbusMaster.connect();
                        } catch (Exception e) {
                            log.error("modbus connect失败"+ip+":"+port, e);
                        }

                    }
                } catch (Exception ex) {
                    log.error("modbus心跳检测失败", ex);
                } finally {
                    try {
                        Thread.sleep(5000);
                    } catch (Exception e) {
                    }
                }
            }
        }
    }
}
